use std::borrow::Borrow;
use std::io;
use chrono::Local;
use rustc_serialize::hex::ToHex;
use sha1::{Sha1, Digest};
use rand::Rng;

// OSC用户ID
const OSCID: &str = "4721473";
// OSC开源项目ID
const PROJECTID: &str = "47006";
// OSC用户登录后的Cookie
const COOKIE: &str = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";

const DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S";
const SOME_ZERO: Option<char> = Some('0');

#[inline]
fn get_token(characters: &Vec<&str>) -> String {
    let mut random = rand::thread_rng();
    let mut ret = String::new();
    for _ in 0..8 {
        let index: usize = random.gen_range(0..36);
        let ch = characters[index];
        ret.push_str(ch);
    }
    ret
}

#[inline]
fn sha1(data: &str) -> String {
    let mut sh = Sha1::new();
    sh.update(data.as_bytes());
    let hex = sh.finalize();
    let ret = hex.as_slice().to_hex();
    ret
}

#[inline]
fn find(characters: &Vec<&str>) -> String {
    let mut counter = 0;
    let is_ok = true;
    while is_ok {
        let token = get_token(&characters);
        for _ in 0..1000000 {
            let genkey = format!("{}:{}:{}:{}", PROJECTID, OSCID, counter, token);
            let testres = sha1(genkey.borrow());
            let mut chars = testres.chars();
            if chars.next() == SOME_ZERO &&
                chars.next() == SOME_ZERO &&
                chars.next() == SOME_ZERO &&
                chars.next() == SOME_ZERO &&
                chars.next() == SOME_ZERO &&
                chars.next() == SOME_ZERO {
                return format!("[{{\"user\": \"{}\", \"project\": \"{}\", \"token\": \"{}\", \"counter\": \"{}\"}}]",
                               OSCID, PROJECTID, token, counter);
            }
            counter += 1;
        }
    }
    String::new()
}

#[inline]
fn send(characters: &Vec<&str>) {
    let client = reqwest::blocking::Client::new();
    let body = find(&characters);
    let result = client.post("https://www.oschina.net/action/api/pow")
        .header("Cookie", COOKIE)
        .body(body)
        .send();
    let now = Local::now().format(DATE_FORMAT);
    match result {
        Ok(res) => match res.text() {
            Ok(str) => println!("{} Success: {}", now, str),
            Err(err) => println!("{} Error: {}", now, err),
        },
        Err(e) => println!("{} Error: {}", now, e)
    }
}

#[tokio::main(worker_threads = 3)]
async fn main() -> io::Result<()> {
    for _ in 0..3 {
        tokio::task::spawn_blocking(|| {
            let characters: Vec<&str> = vec!["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                                             "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
                                             "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
                                             "u", "v", "w", "x", "y", "z"];
            loop {
                send(&characters);
            }
        });
    }
    Ok(())
}
